<?php

declare(strict_types=1);
/**
 * This file is part of habo_new_media.
 *
 * @link     https://www.berlen.cn/
 * @contact  tech@berlen.cn
 * @license  http://gitlab.rehou.cn/habo/habo_new_media/-/blob/master/README.md
 */

namespace app\service\batch\kuaishou;

use app\enum\CommonEnum;
use app\enum\ConfigEnum;
use app\enum\KsBidEnum;
use app\enum\KuaiShouEnum;
use app\model\KsBidTaskContentModel;
use app\model\KsBidTaskLogModel;
use app\model\KsBidTaskModel;
use app\service\batch\MediaInterface;
use app\service\batch\MediaService;
use app\service\client\AccountService;
use app\service\kuaishou\AdvertiserService;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\Exception;
use think\facade\Db;
use think\Model;

class UnitBidService extends MediaService implements MediaInterface
{
    use UnitBidTrait;

    /**
     * @var string 批量改价锁
     */
    protected const BATCH_KEY = ConfigEnum::CACHE_CONFIG[KuaiShouEnum::BATCH_CONFIG_LOCK_KEY]['unit_bid'];

    /**
     * @var null|KsBidTaskModel 任务模型
     */
    private ?KsBidTaskModel $taskModel;

    /**
     * @var null|KsBidTaskContentModel 任务内容模型
     */
    private ?KsBidTaskContentModel $contentModel;

    /**
     * 组出价修改.
     *
     * @param KsBidTaskModel $task 出价任务
     * @throws DbException
     */
    public function unitBid(KsBidTaskModel $task): bool
    {
        $this->taskModel = $task;
        // 拉取组数据
        if (! $this->getUnitList()) {
            return false;
        }
        return $this->pullUnitFinish();
    }

    /**
     * 拉取完成.
     */
    private function pullUnitFinish(): bool
    {
        if (! $this->taskModel->getKey()) {
            return self::setError('未知组出价模型');
        }

        $this->taskModel->setAttr('is_pull', KsBidTaskModel::PULL_SUCCESS);
        if (! $this->taskModel->save()) {
            return self::setError('更新任务状态失败');
        }
        return $this->unitBidFinish();
    }

    /**
     * 获取组数据.
     */
    private function getUnitList(): bool
    {
        if (! $this->taskModel instanceof KsBidTaskModel || ! $this->taskModel->getKey()) {
            return self::setError('未知组出价修改');
        }

        $token = AccountService::getToken(CommonEnum::KS_MEDIA, $this->taskModel->getAttr('advertiser_id'));
        if (! $token) {
            return self::setError('token获取失败');
        }
        $headers = [
            'Access-Token' => $token,
            'Content-Type' => 'application/json',
        ];
        $params['advertiser_id'] = $this->taskModel->getAttr('advertiser_id');
        $params['put_status_list'] = [1, 2];
        $params['page_size'] = 500;

        $page = 0;
        while (true) {
            $params['page'] = ++$page;
            $result = AdvertiserService::getUnitList($headers, $params);
            if ($result === false) {
                return false;
            }
            if (count($result['data']['details']) == 0) {
                return true;
            }
            if (! $this->saveUnit($result['data']['details'])) {
                return false;
            }
            unset($result);
        }
    }

    /**
     *  保存组.
     * @param array $list 组信息队列
     */
    private function saveUnit(array $list = []): bool
    {
        if (! $list) {
            return true;
        }
        foreach ($list as $v) {
            //  判断是否符合
            if (! $this->isMatchType($v)) {
                continue;
            }
            // 修改出价
            if ($this->taskModel->getAttr('type') != KsBidEnum::TASK_TYPE_FUR) {
                // 验证出价是否相同不需要修改
                if ($v['cpa_bid'] == $this->taskModel->getAttr('cpa_bid') * 1000 && $v['deep_conversion_bid'] == $this->taskModel->getAttr('deep_conversion_bid') * 1000) {
                    continue;
                }
                $bid['deep_conversion_bid'] = $this->taskModel->getAttr('deep_conversion_bid') * 1000;
            } else {
                // 验证出价是否相同不需要修改
                if ($v['cpa_bid'] == $this->taskModel->getAttr('cpa_bid') * 1000) {
                    continue;
                }
            }

            Db::startTrans();
            try {
                // 生成出价记录以及日志
                $this->contentModel = $this->setBidContent($v);
                if (! $this->contentModel instanceof KsBidTaskContentModel || ! $this->contentModel->getKey()) {
                    throw new Exception('生成出价记录失败');
                }

                if ($this->contentModel->getAttr('status')) {
                    Db::commit();
                    continue;
                }

                $logModel = $this->setBidLog();

                if (! $logModel instanceof KsBidTaskLogModel || ! $logModel->getKey()) {
                    throw new Exception(self::getError(0));
                }
                Db::commit();
            } catch (\Exception $e) {
                Db::rollback();
                return self::setError($e->getMessage());
            }
        }

        return true;
    }

    /**
     * 保存出价修改信息.
     *
     * @param array $data 组数据信息
     * @return bool|KsBidTaskContentModel
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    private function setBidContent(array $data = [])
    {
        if (! $this->taskModel instanceof KsBidTaskModel || ! $this->taskModel->getKey()) {
            return self::setError('未知组出价修改');
        }
        $model = (new KsBidTaskContentModel())->where([
            ['type', '=', $this->taskModel->getAttr('type')],
            ['task_id', '=', $this->taskModel->getKey()],
            ['ks_unit_id', '=', $data['unit_id']],
        ])->find();
        if (! $model instanceof KsBidTaskContentModel || ! $model->getKey()) {
            $model = new KsBidTaskContentModel();
            $model->setAttrs([
                'task_id' => $this->taskModel->getKey(),
                'ks_campaign_id' => $data['campaign_id'] ?? 0,
                'ks_unit_id' => $data['unit_id'] ?? 0,
                'ks_unit_name' => $data['unit_name'] ?? '',
                'type' => $this->taskModel->getAttr('type'),
                'ocpx_action_type' => $this->taskModel->getAttr('ocpx_action_type'),
                'deep_conversion_type' => $this->taskModel->getAttr('deep_conversion_type'),
                'cpa_bid' => $this->taskModel->getAttr('cpa_bid'),
                'deep_conversion_bid' => $this->taskModel->getAttr('deep_conversion_bid'),
                'status' => KsBidTaskContentModel::STATUS_WAIT,
            ]);

            $model->save();
        }
        return $model;
    }

    /**
     * 创建出价记录.
     *
     * @return null|array|bool|KsBidTaskLogModel|mixed|Model
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    private function setBidLog()
    {
        if (! $this->contentModel instanceof KsBidTaskContentModel || ! $this->contentModel->getKey()) {
            return self::setError('未知组信息');
        }
        $model = (new KsBidTaskLogModel())->where([
            ['content_id', '=', $this->contentModel->getKey()],
        ])->find();
        if (! $model instanceof KsBidTaskLogModel) {
            $model = new KsBidTaskLogModel();
        }
        if (! $model->getKey()) {
            $model->setAttrs([
                'content_id' => $this->contentModel->getKey(),
            ]);
            if (! $model->save()) {
                return self::setError('生成出价日志失败');
            }
        }
        return $model;
    }

    /**
     * 判读是否该类型.
     *
     * @param array $data 组数据
     */
    private function isMatchType(array $data = []): bool
    {
        if ($data['unit_source']) {
            return self::setError('托管组不修改');
        }

        if (! $this->taskModel instanceof KsBidTaskModel || ! $this->taskModel->getKey()) {
            return self::setError('未知组出价修改');
        }
        $type = $this->taskModel->getAttr('type');
        if ($type == KsBidEnum::TASK_TYPE_ONE && isset($data['enhance_conversion_type']) && $data['enhance_conversion_type'] == KsBidEnum::ENHANCE_TYPE_CONVERSION) {
            return false;
        }
        $fields = KsBidEnum::TASK_TYPE_FIELD[$type];
        foreach ($fields as $field => $value) {
            if (! isset($data[$field]) || $data[$field] != $value) {
                return false;
            }
        }
        return true;
    }
}
